/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../impl/head.h"
#include "../list/head.h"
#include "threads_impl.h"

/////////////////////////////////////////////////////////////////////////////////////////
// 
namespace xos_box
{
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static threads_impl::ITEM_DATA xos_item_data;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    threads_impl::threads_impl()
    {
        init_data();
    }

    threads_impl::~threads_impl()
    {
        term();
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    int threads_impl::get_xos_item_data( ITEM_DATA*& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // nNum = 0, means cpu core num
    // if not set, default is 0
    // 
    int threads_impl::set_max_thread_num( int nNum )
    {
        int ret = 0;

        m_nMaxThreadNum = nNum;

        if( ( 0 == ret ) && ( nNum <= 0 ) )
        {
            m_nMaxThreadNum = get_cpu_num();
        }

        return ret;
    }

    int threads_impl::get_max_thread_num()
    {
        return m_nMaxThreadNum;
    }

    i_callback * threads_impl::get_thread_dispatch()
    {
        xos_stl::auto_lock<i_list> lock( m_pThreadList );

        i_callback * pThread = 0;
        int nMsgNum = 99999999;

        // 取出缓冲消息队伍最短的线程
        {
            i_callback * pT = 0;
            for( m_pThreadList->reset(); ( pT = ( xos_box::i_callback * )m_pThreadList->next() ); )
            {
                int num = pT->size();
                if( nMsgNum > num )
                {
                    nMsgNum = num;
                    pThread = pT;
                }
            }
        }

        // 注意，这里没错，逻辑如下：
        // 如果取出线程的队列长度超过设定值，并且线程数没有超过最大值，就创建一个新线程
        // 否则，就使用找出来的线程
        {
            i_callback * pT = helper_new_thread( nMsgNum );
            if( pT )
            {
                pThread = pT;
            }
        }

        return pThread;
    }

    i_callback * threads_impl::get_thread_in_order()
    {
        xos_stl::auto_lock<i_list> lock( m_pThreadList );

        xos::i_release * pRelease = 0;
        i_callback * pThread = 0;
        int nMsgNum = 99999999;

        // 如果没走到队尾，并且线程队列非空，就能取线程出来
        if( !pRelease )
        {
            pRelease = m_pThreadListIter->next();
        }

        // 如果走到队尾了，或队伍为空，就创建一个线程
        if( !pRelease )
        {
            pRelease = helper_new_thread( nMsgNum );
            if( pRelease )
            {
                m_pThreadListIter->next();
            }
        }

        // 如果到队尾了，并且线程数量到达上限
        // 那就从队头开始取线程
        if( !pRelease )
        {
            m_pThreadListIter->reset();
            pRelease = m_pThreadListIter->next();
        }

        if( pRelease )
        {
            pThread = ( i_callback * )pRelease;
        }

        return pThread;
    }

    i_callback * threads_impl::notify( i_msg *& pMsg )
    {
        i_callback * pRet = 0;
        pRet = helper_notify( pMsg );
        return pRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    i_callback * threads_impl::helper_new_thread( int nMsgNum )
    {
        i_callback * pThread = 0;

        if( ( nMsgNum > m_nTaskNumForNewThread ) && ( m_pThreadList->size() < m_nMaxThreadNum ) )
        {
            xos_box::i_callback * pT = mgr::get()->callback();
            m_pThreadList->push_back( pT );
            pThread = pT;
        }

        return pThread;
    }

    i_callback * threads_impl::helper_notify( i_msg *& pMsg )
    {
        xos_box::i_callback * pThread = 0;
        
        {
            pThread = get_thread_dispatch();
            pThread->notify( pMsg );
        }

        return pThread;
    }

    int threads_impl::get_cpu_num()
    {
        int ret = 0;

        ret = mgr::container()->misc()->get_cpu_num();

        return ret;
    }

    int threads_impl::init_data()
    {
        int ret = 0;

        m_nTaskNumForNewThread = 3;
        m_nMaxThreadNum = 0;

        m_pThreadListIter = 0;
        m_pThreadList = 0;

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    int threads_impl::init()
    {
        int ret = 0;

        if( 0 == ret )
        {
            list_impl::T* pT = 0;
            xos_stl::init_pool_item( pT );
            m_pThreadList = pT;
        }

        if( 0 == ret )
        {
            m_pThreadListIter = m_pThreadList->begin();
        }

        if( 0 == ret )
        {
            set_max_thread_num( m_nMaxThreadNum );
        }

        return ret;
    }

    int threads_impl::term()
    {
        int ret = 0;
        xos_stl::release_interface( m_pThreadListIter );
        xos_stl::release_interface( m_pThreadList );
        init_data();
        return ret;
    }

} // xos
